/*
 * Copyright 2020-2030 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.gitee.minimalismstyle.fresh.common.swagger.config

import com.google.common.base.Predicates
import com.gitee.minimalismstyle.fresh.common.swagger.config.SwaggerProperties.GlobalOperationParameter
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import springfox.documentation.builders.ApiInfoBuilder
import springfox.documentation.builders.ParameterBuilder
import springfox.documentation.builders.PathSelectors
import springfox.documentation.builders.RequestHandlerSelectors
import springfox.documentation.schema.ModelRef
import springfox.documentation.service.ApiInfo
import springfox.documentation.service.Contact
import springfox.documentation.service.Parameter
import springfox.documentation.service.SecurityReference
import springfox.documentation.spi.DocumentationType
import springfox.documentation.spi.service.contexts.SecurityContext
import springfox.documentation.spring.web.plugins.Docket
import springfox.documentation.swagger2.annotations.EnableSwagger2
import java.util.*
import java.util.function.Predicate

/**
 * Swagger2统一配置
 */
@Configuration
@EnableSwagger2
@EnableConfigurationProperties(SwaggerProperties::class)
class SwaggerConfig(val swaggerProperties: SwaggerProperties ) {

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(name = ["fresh.swagger.enabled"], matchIfMissing = true)
    fun createRestApi(): Docket {

        // base-path处理
        // 当没有配置任何path的时候，解析/**
        if (swaggerProperties.basePath.isEmpty()) {
            swaggerProperties.basePath.add("/**")
        }
        val basePath = ArrayList<Predicate<String>>()
        for (path in swaggerProperties.basePath) {
            basePath.add(PathSelectors.ant(path))
        }

        // exclude-path处理
        val excludePath = ArrayList<Predicate<String>>()
        for (path in swaggerProperties.excludePath) {
            excludePath.add(PathSelectors.ant(path))
        }

        val docketForBuilder = Docket(DocumentationType.SWAGGER_2)
                .host(swaggerProperties.host)
                .apiInfo(apiInfo())
                .securityContexts(Collections.singletonList(securityContext()))
                .globalOperationParameters(swaggerProperties.globalOperationParameters?.let {
                    buildGlobalOperationParametersFromSwaggerProperties(
                            it)
                })

        val docket = docketForBuilder.select()
                .apis(RequestHandlerSelectors.basePackage(swaggerProperties.basePackage))
                .paths(PathSelectors.any()
                ).build()

        val ignoredParameterTypes: Array<Class<*>> = swaggerProperties.ignoredParameterTypes.toTypedArray()
        docket.ignoredParameterTypes(*ignoredParameterTypes)

        return docket
    }

    private fun apiInfo(): ApiInfo? {
        return ApiInfoBuilder()
                .title(swaggerProperties.title)
                .description(swaggerProperties.description)
                .version(swaggerProperties.version)
                .license(swaggerProperties.license)
                .licenseUrl(swaggerProperties.licenseUrl)
                .contact(Contact(swaggerProperties.contact.name,
                        swaggerProperties.contact.url,
                        swaggerProperties.contact.email))
                .termsOfServiceUrl(swaggerProperties.termsOfServiceUrl)
                .build()
    }

    private fun securityContext(): SecurityContext? {
        return SecurityContext.builder()
                .securityReferences(defaultAuth())
                .forPaths(PathSelectors.regex(swaggerProperties.authorization.authRegex))
                .build()
    }

    private fun defaultAuth(): List<SecurityReference?>? {
        return Collections.singletonList(SecurityReference.builder()
                .reference(swaggerProperties.authorization.name)
                .scopes(swaggerProperties.authorization.authorizationScopeList.toTypedArray()).build())
    }

    private fun buildGlobalOperationParametersFromSwaggerProperties(
            globalOperationParameters: List<GlobalOperationParameter>): List<Parameter?>? {
        val parameters: MutableList<Parameter?> = mutableListOf()
        if (Objects.isNull(globalOperationParameters)) {
            return parameters
        }
        for (globalOperationParameter in globalOperationParameters) {
            parameters.add(ParameterBuilder()
                    .name(globalOperationParameter.name)
                    .description(globalOperationParameter.description)
                    .modelRef(ModelRef(globalOperationParameter.modelRef))
                    .parameterType(globalOperationParameter.parameterType)
                    .required(globalOperationParameter.required)
                    .build())
        }
        return parameters
    }
}